package xq.gwt.ui.crud;


import xq.gwt.mvc.model.BooleanPropertyModel;
import xq.gwt.mvc.model.CommandModel;
import xq.gwt.mvc.model.EntityChangedEvent;
import xq.gwt.mvc.model.EntityChangedListener;
import xq.gwt.mvc.model.EntityModel;
import xq.gwt.mvc.model.PersistableEntityModel;
import xq.gwt.ui.crud.localization.CrudDictionary;
import xq.gwt.ui.rpc.AsyncCallbackAdapter;
import xq.gwt.ui.widgets.entity.EntityArrayPresenter;
import xq.gwt.ui.widgets.entity.EntitySelectedListener;
import xq.gwt.ui.widgets.entity.FormEntityPresenter;
import xq.gwt.ui.widgets.factory.DefaultEntityPresenterFactory;
import xq.gwt.ui.widgets.factory.EntityPresenterFactory;
import xq.gwt.ui.widgets.feedback.DefaultFeedbackPresenter;
import xq.gwt.ui.widgets.feedback.FeedbackService;

import com.smartgwt.client.data.Criteria;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.grid.ListGrid;
import com.smartgwt.client.widgets.layout.VLayout;
import com.smartgwt.client.widgets.layout.VStack;

public class EntityArrayEditor implements EntitySelectedListener, EntityChangedListener, PageMovedListener, EntityListReader{
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 2195491357861528642L;
	protected EntityArrayPresenter entityListPresenter;
	protected VStack detailStack = new VStack();	
	protected VLayout layout;
	protected EntityEditorModel editorModel;
	protected VLayout header = new VLayout();
	private PersistableEntityModel prototypeEntity;
	protected FormEntityPresenter entityEditor;
	protected PagerPresenter pagerPresenter;
	private ListGrid entityGrid;
	private boolean blockEntityChangedEvents;
	private boolean blockSelectionChanged;
	private boolean blockRefresh;
	private boolean dataFetchInitialised;
	private PropertyListItemProvider listItemProvider;
	private FilterModel filter;
	protected OrderByModel orderBy[]; 
	private CrudPermisions permissions;
	private int editorColumns;
	private boolean autoFetch = true;
	private boolean isPageMode;	
	protected PagerModel pagerModel = new PagerModel();
	protected VLayout entityListlayout;
	private boolean pagingEnabled = true;
	private EntityPresenterFactory entityPresenterFactory = new DefaultEntityPresenterFactory();
	
	public EntityArrayEditor(PersistableEntityModel prototypeEntity, PropertyListItemProvider listItemProvider, CrudPermisions permissions) {		
		
		this.prototypeEntity = prototypeEntity;
		this.listItemProvider = listItemProvider;
		this.permissions = permissions;		
		init();
	}
	
	public EntityArrayEditor(PersistableEntityModel prototypeEntity, PropertyListItemProvider listItemProvider, CrudPermisions permissions, FilterModel filter) {		
		
		this.prototypeEntity = prototypeEntity;
		this.listItemProvider = listItemProvider;
		this.permissions = permissions;		
		this.filter = filter;
		init();
	}
	
	public EntityArrayEditor(PersistableEntityModel prototypeEntity, PropertyListItemProvider listItemProvider, CrudPermisions permissions, FilterModel filter, int editorCoumns) {		
		
		this.prototypeEntity = prototypeEntity;
		this.listItemProvider = listItemProvider;
		this.permissions = permissions;		
		this.filter = filter;
		init();
		this.editorColumns = editorCoumns;
	}
	
	public EntityArrayEditor(PersistableEntityModel prototypeEntity, PropertyListItemProvider listItemProvider, CrudPermisions permissions, FilterModel filter, int editorCoumns, OrderByModel[] orderBy) {		
		
		this.prototypeEntity = prototypeEntity;
		this.listItemProvider = listItemProvider;
		this.permissions = permissions;		
		this.filter = filter;
		init();
		this.editorColumns = editorCoumns;
		this.orderBy = orderBy;
	}
	
	private void init(){
		FeedbackService.getFeedbackPresenter().showFeedback(null);
		editorModel = new EntityEditorModel();
		editorModel.getNewEntityCommand().setPrototype(prototypeEntity);
		editorModel.addEntityChangedListener(this);
		if(permissions != null){
			editorModel.setInsertDenied(permissions.insertDenied);
			editorModel.setUpdateDenied(permissions.updateDenied);
			editorModel.setDeleteDenied(permissions.deleteDenied);
		}
		
		entityGrid = new ListGrid();		
		entityGrid.setShowFilterEditor(true);
		
		entityListPresenter = new EntityArrayPresenter(entityGrid, this.prototypeEntity);		
		entityListPresenter.setSelectionListener(this);
		entityListPresenter.getCanvas().setHeight("100%");				
		entityListlayout = new VLayout();
		entityListlayout.setHeight("70%");
		entityListlayout.setShowResizeBar(true);
		entityListlayout.addMember(entityListPresenter.getCanvas());
		
		showEntityEditor();		
			
		layout = new VLayout();		
		layout.setWidth("100%");
		layout.setHeight("100%");
		header.setHeight(1);
		header.setWidth100();				
		layout.setMembers(header,entityListlayout,detailStack);		
	}
		
	public ListGrid getEntityGrid() {
		return entityGrid;
	}

	protected void showPager() {
		pagerPresenter = new PagerPresenter();
		pagerPresenter.setPagerModel(pagerModel);
		pagerPresenter.getCanvas().setWidth("100%");
		entityListlayout.addMember(pagerPresenter.getCanvas());
	}
	
	public void setEntities(EntityModel[] entities){		
		entityListPresenter.setEntities(entities);		
	}
	
	public void clear(){
		if(this.entityListPresenter != null){
			this.entityListPresenter.clear();
		}
	}
	
	public void editEntity(PersistableEntityModel entity){
		if(editorModel != null){
			blockRefresh = true;
			editorModel.setEntity(entity);
			blockRefresh = false;
		}
	}
	
		
 	public boolean isAutoFetch() {
		return autoFetch;
	}

	public void setAutoFetch(boolean autoFetch) {
		this.autoFetch = autoFetch;
	}
		
	public boolean isPagingEnabled() {
		return pagingEnabled;
	}

	public void setPagingEnabled(boolean pagingEnabled) {
		this.pagingEnabled = pagingEnabled;
	}

	public FilterModel getFilter() {
		return filter;
	}

	public void setFilter(FilterModel filter) {
		this.filter = filter;		
	}
	
	public void setOrderBy(OrderByModel[] orderBy){
		this.orderBy = orderBy;		
	}

	public void fetchData(){
		dataFetchInitialised = true;	
		clearPaging();
		CrudServiceProvider.persistanceService.fetch(prototypeEntity,filter, orderBy,null,null, new EntitiesFetched());
		FeedbackService.getFeedbackPresenter().showFeedback(CrudDictionary.Constants().fetching_data_msg());
	}
			
	private void fetchPagedData(){
		dataFetchInitialised = true;
		CrudServiceProvider.persistanceService.fetch(prototypeEntity,filter,orderBy,pagerModel.getOffset().getValue(),pagerModel.getIncrement().getValue(), new EntitiesFetched());
		FeedbackService.getFeedbackPresenter().showFeedback(CrudDictionary.Constants().fetching_data_msg());
	}
	
	
	public EntityPresenterFactory getEntityPresenterFactory() {
		return entityPresenterFactory;
	}

	public void setEntityPresenterFactory(
			EntityPresenterFactory entityPresenterFactory) {
		this.entityPresenterFactory = entityPresenterFactory;
	}

	private void refresh(){
		if(blockRefresh)
			return;
		if(dataFetchInitialised)
			return;
		
		if(isPageMode){
			fetchPagedData();
		} else {
			fetchData();
		}
		
							
	}
	
	private class EntitiesFetched extends AsyncCallbackAdapter<FetchedEntities>{
		
		@Override
		public void onSuccess(FetchedEntities result) {
			dataFetchInitialised = false;
			Criteria  filterCriteria = entityGrid.getFilterEditorCriteria();				
			blockSelectionChanged = true;
			setEntities(result.entities);
			entityGrid.setFilterEditorCriteria(filterCriteria);
			entityGrid.filterByEditor();
			
			//Select edited entity					
			PersistableEntityModel editedEntity = editorModel.getEntity();
			if (editedEntity != null && !editedEntity.isNew()){
				entityListPresenter.selectRecord(editedEntity.getIdentifier());
			}
			else{
				entityListPresenter.selectRecord(null);
			}			
			blockSelectionChanged = false;
			
			FeedbackService.getFeedbackPresenter().showFeedback(null);
			if(result.entities != null){				
				pagerModel.getTotalRowCount().setValue(result.outOfTotalEntities);
				if(result.outOfTotalEntities > result.entities.length){					
					if(isPagingEnabled()){
						startPaging(result.entities.length);					
					} else {
						String feedback = CrudDictionary.Constants().records_fetched_out_of().replace("%s1", String.valueOf(result.entities.length)).replace("%s2", String.valueOf(result.outOfTotalEntities));
						FeedbackService.getFeedbackPresenter().showFeedback(feedback);
					}
				}				
			}
		}

		@Override
		public void onFailure(Throwable caught) {
			dataFetchInitialised = false;
			super.onFailure(caught);
		}
		
		
	}
		
	protected void startPaging(Integer increment){
		if(!isPageMode){
			showPager();
			pagerModel.getIncrement().setValue(increment);
			pagerModel.setPageMovedListener(this);
			isPageMode = true;
		}		
	}
	
	protected void clearPaging(){
		if(pagerModel != null){			
		    pagerModel.setPageMovedListener(null);
			pagerModel = new PagerModel();				   
		}
		isPageMode = false;		
	}
	
	
	@Override
	public void pageMoved() {
		fetchPagedData();		
	}

	@Override
	public void entitySelected(EntityModel entity) {
		if (blockSelectionChanged)
			return;
		blockEntityChangedEvents = true;
		PersistableEntityModel entityToEdit = null;
		if(entity != null){
			try {
				entityToEdit = ((PersistableEntityModel)entity).copy();
			} catch (Exception e) {				
				e.printStackTrace();
			}
		}
		editorModel.setEntity(entityToEdit);		
		showEntityEditor();	
		blockEntityChangedEvents = false;
	}
		
	public Canvas getCanvas(){
		return layout;
	}

	@Override
	public void entityChanged(EntityChangedEvent event) {
		if (blockEntityChangedEvents){
			return;
		}		
		if (event.getSource().equals(this.editorModel)) {
			entityListPresenter.selectRecord(null);
			showEntityEditor();
			PersistableEntityModel editedEntity = this.editorModel.getEntity();
			
			if (isAutoFetch()) {
				if(editedEntity != null && editedEntity.isNew())
					return;
			} else { 
				if(editedEntity == null)
					return;
				if (editedEntity.isNew()) 
					return;
				if (!entityListPresenter.containsRecord(editedEntity.getIdentifier()))
					return;				
			}
			refresh();
			
		}
		
	}
	
	protected void showEntityEditor(){		
		ShowEntityEditorCommand showEntityEditorCommand = new ShowEntityEditorCommand();
		if (listItemProvider != null && (editorModel.getEntity() != null)){
			listItemProvider.setListItemsAsync(editorModel.getEntity(), showEntityEditorCommand);
		}
		else {
			try {
				showEntityEditorCommand.execute();
			} catch (Exception e) {
				DefaultFeedbackPresenter presenter = new DefaultFeedbackPresenter();
				presenter.showFeedback(e.getMessage());
			}
		}		
	}

	protected void afterShowEntityEditor(){
		
	}
	
	private class ShowEntityEditorCommand implements CommandModel{
		
		/**
		 * 
		 */
		private static final long serialVersionUID = -2546609245492525488L;
		BooleanPropertyModel enabled = new BooleanPropertyModel();
		
		public ShowEntityEditorCommand(){
			enabled.setValue(true);
		}
		
		@Override
		public void execute() throws Exception {
			entityEditor = entityPresenterFactory.buildPresenter(editorModel);
			if(editorColumns != 0){
				entityEditor.setNumCols(editorColumns);
			}
						
			detailStack.setMembers(entityEditor.getCanvas());
			detailStack.setHeight("50%");
			afterShowEntityEditor();			
		}

		@Override
		public String getCaption() {			
			return "Show entity editor";
		}

		@Override
		public String getName() {			
			return "ShowEntityEditor";
		}

		@Override
		public BooleanPropertyModel isEnabled() {			
			return enabled;
		}

		@Override
		public String getFeedback() {			
			return null;
		}				
	}

	@Override
	public void setEntityReceiver(EntityReceiver receiver) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void setPrototypeEntity(PersistableEntityModel entity) {
		this.prototypeEntity = entity;
		
	}

	@Override
	public void read() {
		fetchData();
		
	}
	
}
